package com.smart.expand.proxy.cglib;


import com.smart.expand.proxy.AnnotationProcessor;
import com.smart.expand.proxy.BaseProxy;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import org.springframework.core.annotation.AnnotationUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.ParameterizedType;
import java.util.*;

/**
 * @author jiang
 */
public class CglibDynamicProxy extends BaseProxy implements MethodInterceptor {

    private static CglibDynamicProxy cglibDynamicProxy = new CglibDynamicProxy();

    private CglibDynamicProxy(){

    }

    public static CglibDynamicProxy getInstance(){
        return cglibDynamicProxy;
    }


    public Object newProxyInstance(Object object) {
        return Enhancer.create(object.getClass(), this);
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        Object ret = null;
        Map<Class<? extends Annotation>, AnnotationProcessor<? extends Annotation>> map = new HashMap<>();
        for (AnnotationProcessor<? extends Annotation> annotationProcessor : annotationProcessors) {
            Class<? extends Annotation> annotationClass = (Class<? extends Annotation>) ((ParameterizedType) annotationProcessor.getClass().getGenericInterfaces()[0]).getActualTypeArguments()[0];
            Annotation annotation1 = AnnotationUtils.findAnnotation(method, annotationClass);
            if (annotation1 != null) {
                map.put(annotationClass, annotationProcessor);
            }
            Parameter[] parameters = method.getParameters();
            for (int i = 0; i < parameters.length; i++) {
                Parameter parameter = parameters[i];
                Annotation annotation2 = AnnotationUtils.findAnnotation(parameter, annotationClass);
                if (annotation2 != null) {
                    objects[i] = annotationProcessor.onParameter((Class) annotationClass, o, method, parameter, objects[i]);
                }
            }
        }
        for (Map.Entry<Class<? extends Annotation>, AnnotationProcessor<? extends Annotation>> classAnnotationProcessorEntry : map.entrySet()) {
            ret = classAnnotationProcessorEntry.getValue().onMethod((Class) classAnnotationProcessorEntry.getKey(), o, method, objects);
            if (ret != null) {
                return ret;
            }
        }
        for (Map.Entry<Class<? extends Annotation>, AnnotationProcessor<? extends Annotation>> classAnnotationProcessorEntry : map.entrySet()) {
            objects = classAnnotationProcessorEntry.getValue().methodBefore((Class) classAnnotationProcessorEntry.getKey(), o, method, objects);
        }
        ret = methodProxy.invokeSuper(o, objects);
        for (Map.Entry<Class<? extends Annotation>, AnnotationProcessor<? extends Annotation>> classAnnotationProcessorEntry : map.entrySet()) {
            ret = classAnnotationProcessorEntry.getValue().methodAfter((Class) classAnnotationProcessorEntry.getKey(), o, method, ret);
        }
        return ret;
    }
}